package gui;

import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JLabel;

/**
 * @author Ramy Refky 
 * */
public class ComponentPin extends JLabel implements MouseListener {

	private static final long serialVersionUID = 1L;
	
	int startX;
	int startY;
	int endX;
	int endY;
	MainGui mg;
	ComponentGraphics cg;
	ArrayList<Wire> linkedWires;
	boolean type;
	int connections;
	boolean grouped;
	
	int id;
	/**
	 *  Constructor initializes ComponentPin with given parent component and bounds.
	 *  @param MainGui, ComponentGraphics, startX, startY, endX, endY
	 */
	public ComponentPin(MainGui mg,ComponentGraphics cg,int startX,int startY,int endX,int endY){
		this.mg=mg;
		this.cg=cg;
		this.startX=startX;
		this.startY=startY;
		this.endX=endX;
		this.endY=endY;
		linkedWires=new ArrayList<Wire>();
		id=0;
		int width= endX-startX;
		int height= endY-startY+4;
		setBounds(startX,startY,width,height);
		setBorder(BorderFactory.createLineBorder(Color.black,2));
		
		connections=0;
		setVisible(true);
		addMouseListener(this);
		repaint();
	}
	/**
	 * Sets the ComponentPin's ID to a given integer value
	 * */
	public void setID(int id){
		this.id=id;
	}
	/**
	 * Gets the ComponentPin's ID value
	 * @return int
	 * */
	public int getID(){
		return id;
	}
	/**
	 * Adjusts the ComponentPin's bounds according to new parameters.
	 * */
	public void zoomAdjust(int startX,int startY,int endX,int endY){
		
		this.startX=startX;
		this.startY=startY;
		this.endX=endX;
		this.endY=endY;
		
		int width= endX-startX;
		int height= endY-startY+4;;
		
		setBounds(startX,startY,width,height);
	}
	/**
	 * Gets the ComponentPin's starting X and Y coordinates in the form of a Point.
	 * @return Point
	 * */
	public Point getStartPoint(){
		return new Point(startX+getParent().getX(),startY+getParent().getY()+2);
	}
	/**
	 * Gets the ComponentPin's ending X and Y coordinates in the form of a Point.
	 * @return Point
	 * */
	public Point getEndPoint(){
		return new Point(endX+getParent().getX(),endY+getParent().getY()+2);
	}
	/**
	 * Gets the ComponentPin's parent component.
	 * @return ComponentGraphics
	 * */
	public ComponentGraphics getParentComponent(){
		return cg;
	}
	/**
	 * Sets the ComponentPin's type as an input pin.
	 * */
	public void setTypeAsInput(){
		type=false;
	}
	/**
	 * Sets the ComponentPin's type as an output pin.
	 * */
	public void setTypeAsOutput(){
		type=true;
	}
	/**
	 * Sets the ComponentPin to be grouped.
	 * */
	public void setAsGrouped(){
		grouped=true;
	}
	/**
	 * Gets the total number of grouped pins that are grouped with this ComponentPin.
	 * @return int
	 * */
	public int getGroupCount(){
		if(grouped){
			if(type){
				return getParentComponent().getNumberOfOutputs();
			}
			else{
				return getParentComponent().getNumberOfInputs();
			}
		}
		else{
			return 1;
		}
	}
	/**
	 * Adds a connection to the ComponentPin, indicating that there is a wire connecting it.
	 * */
	public void addConnection(){
		connections++;
	}
	/**
	 * Removes a connection from the ComponentPin, indicating that a wire has been deleted from that pin.
	 * */
	public void removeConnection(){
		connections--;
	}
	/**
	 * Sets the ComponentPin's number of connections to a given value.
	 * */
	public void setConnections(int con){
		connections=con;
	}
	/**
	 * Gets the ComponentPin's type, which is a boolean value, if true then it is an output pin.
	 * Otherwise, it is set as an input pin.
	 * @return boolean
	 * */
	public boolean getType(){
		return type;
	}
	/**
	 * Checks if the ComponentPin is grouped with other ComponentPins.
	 * @return boolean
	 * */
	public boolean isGrouped(){
		return grouped;
	}
	/**
	 * Gets the ComponentPin's number of connections, indicating the number of wires connected to that pin.
	 * @return int
	 * */
	public int getConnections(){
		return connections;
	}
	@Override
	public void mouseClicked(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) {
		setBorder(BorderFactory.createLineBorder(Color.green,2));
	}
	@Override
	public void mouseExited(MouseEvent e) {
		setBorder(BorderFactory.createLineBorder(Color.black,2));
	}
	@Override
	public void mousePressed(MouseEvent e) {
		
		if(mg.getActions().getCurrentAction()==6){
			if(!mg.getActions().checkIfAddedFirstWirePoint()){
				mg.getActions().setFirstPin(this);
				mg.getActions().setAddedFirstWirePoint(true);
			}
			else{
				if(mg.getActions().checkIfAddedFirstWirePoint()){
					mg.getActions().setSecondPin(this);
					mg.getActions().setAddedFirstWirePoint(false);
					//output to input or input to output only
					if(mg.getActions().getFirstPin().getType()!=mg.getActions().getSecondPin().getType() && 
							!mg.getActions().getFirstPin().getParentComponent().
								equals(mg.getActions().getSecondPin().getParentComponent())){
						
						//Wire grouping
						if(mg.getActions().getFirstPin().getGroupCount()==mg.getActions().getSecondPin().getGroupCount()){
							
							//if input to output and input not yet connected
							if(!mg.getActions().getFirstPin().getType() && 
									mg.getActions().getFirstPin().getConnections()==0){
								
								
								if(mg.getActions().getFirstPin().isGrouped()||mg.getActions().getSecondPin().isGrouped()){
									
									ArrayList<ComponentPin> firstPins = mg.getActions().
											getFirstPin().getParentComponent().getInputPins();
									ArrayList<ComponentPin> secondPins = mg.getActions().
											getSecondPin().getParentComponent().getOutputPins();
									
									Wire w = new Wire(mg,firstPins.get(0),secondPins.get(0));
									w.setAsGrouped();
									linkedWires.add(w);
									mg.getWorkArea().getWiringGrid().addWire(w);
									for(int i=0;i<firstPins.size();i++){	
										firstPins.get(i).addConnection();
										secondPins.get(i).addConnection();		
									}	
								}
								else{
									
									Wire w = new Wire(mg,mg.getActions().getFirstPin(),
											mg.getActions().getSecondPin());
									
									linkedWires.add(w);
									mg.getWorkArea().getWiringGrid().addWire(w);
									
									mg.getActions().getFirstPin().addConnection();
									mg.getActions().getSecondPin().addConnection();	
								}
							}
							if(!mg.getActions().getSecondPin().getType() && 
									mg.getActions().getSecondPin().getConnections()==0){
								
								if(mg.getActions().getFirstPin().isGrouped()||mg.getActions().getSecondPin().isGrouped()){
									
									ArrayList<ComponentPin> firstPins = mg.getActions().
											getFirstPin().getParentComponent().getOutputPins();
									ArrayList<ComponentPin> secondPins = mg.getActions().
											getSecondPin().getParentComponent().getInputPins();
									
									Wire w = new Wire(mg,secondPins.get(0),firstPins.get(0));
									w.setAsGrouped();
									linkedWires.add(w);
									mg.getWorkArea().getWiringGrid().addWire(w);
										
									for(int i=0;i<firstPins.size();i++){
										firstPins.get(i).addConnection();
										secondPins.get(i).addConnection();		
									}	
								}
								else{
									
									Wire w = new Wire(mg,mg.getActions().getSecondPin(),
											mg.getActions().getFirstPin());
									
									linkedWires.add(w);
									mg.getWorkArea().getWiringGrid().addWire(w);
									
									mg.getActions().getFirstPin().addConnection();
									mg.getActions().getSecondPin().addConnection();	
								}
							}	
						}
					}
				}
			}
			mg.getWorkArea().drawIntersections();
			mg.getWorkArea().drawWireGroupDisplays();
			mg.getWorkArea().repaint();
		}
	}
	@Override
	public void mouseReleased(MouseEvent e) {}
}
